/**
 *
 * User: Zhikang.Peng
 * Date: 2018/06/20 15:48
 */
package stores

import (
	"gitee.com/thghh/jvmgo/instructions/base"
	"gitee.com/thghh/jvmgo/rtda"
	"gitee.com/thghh/jvmgo/rtda/heap"
)

// 将栈顶int型数值存入指定数组的指定索引位置
type IASTORE struct{ base.NoOperandsInstruction }

func (self *IASTORE) Execute(frame *rtda.Frame) {
	stack := frame.OperandStack()
	value := stack.PopInt()
	index := stack.PopInt()
	arrayRef := stack.PopReference()

	checkNotNil(arrayRef)
	array := arrayRef.Ints()
	checkIndex(len(array), index)
	array[index] = value
}

// 将栈顶long型数值存入指定数组的指定索引位置
type LASTORE struct{ base.NoOperandsInstruction }

func (self *LASTORE) Execute(frame *rtda.Frame) {
	stack := frame.OperandStack()
	value := stack.PopLong()
	index := stack.PopInt()
	arrayRef := stack.PopReference()

	checkNotNil(arrayRef)
	array := arrayRef.Longs()
	checkIndex(len(array), index)
	array[index] = value
}

// 将栈顶float型数值存入指定数组的指定索引位置
type FASTORE struct{ base.NoOperandsInstruction }

func (self *FASTORE) Execute(frame *rtda.Frame) {
	stack := frame.OperandStack()
	value := stack.PopFloat()
	index := stack.PopInt()
	arrayRef := stack.PopReference()

	checkNotNil(arrayRef)
	array := arrayRef.Floats()
	checkIndex(len(array), index)
	array[index] = value
}

// 将栈顶double型数值存入指定数组的指定索引位置
type DASTORE struct{ base.NoOperandsInstruction }

func (self *DASTORE) Execute(frame *rtda.Frame) {
	stack := frame.OperandStack()
	value := stack.PopDouble()
	index := stack.PopInt()
	arrayRef := stack.PopReference()

	checkNotNil(arrayRef)
	array := arrayRef.Doubles()
	checkIndex(len(array), index)
	array[index] = value
}

// 将栈顶引用型数值存入指定数组的指定索引位置
type AASTORE struct{ base.NoOperandsInstruction }

func (self *AASTORE) Execute(frame *rtda.Frame) {
	stack := frame.OperandStack()
	value := stack.PopReference()
	index := stack.PopInt()
	arrayRef := stack.PopReference()

	checkNotNil(arrayRef)
	array := arrayRef.References()
	checkIndex(len(array), index)
	array[index] = value
}

// 将栈顶boolean或byte型数值存入指定数组的指定索引位置
type BASTORE struct{ base.NoOperandsInstruction }

func (self *BASTORE) Execute(frame *rtda.Frame) {
	stack := frame.OperandStack()
	value := stack.PopInt()
	index := stack.PopInt()
	arrayRef := stack.PopReference()

	checkNotNil(arrayRef)
	array := arrayRef.Bytes()
	checkIndex(len(array), index)
	array[index] = int8(value)
}

// 将栈顶char型数值存入指定数组的指定索引位置
type CASTORE struct{ base.NoOperandsInstruction }

func (self *CASTORE) Execute(frame *rtda.Frame) {
	stack := frame.OperandStack()
	value := stack.PopInt()
	index := stack.PopInt()
	arrayRef := stack.PopReference()

	checkNotNil(arrayRef)
	array := arrayRef.Chars()
	checkIndex(len(array), index)
	array[index] = uint16(value)
}

// 将栈顶short型数值存入指定数组的指定索引位置
type SASTORE struct{ base.NoOperandsInstruction }

func (self *SASTORE) Execute(frame *rtda.Frame) {
	stack := frame.OperandStack()
	value := stack.PopInt()
	index := stack.PopInt()
	arrayRef := stack.PopReference()

	checkNotNil(arrayRef)
	array := arrayRef.Shorts()
	checkIndex(len(array), index)
	array[index] = int16(value)
}

// 检查数组对象是否为空
func checkNotNil(ref *heap.Object) {
	if ref == nil {
		panic("java.lang.NullPointerException")
	}
}

// 检查数组下标是否越界
func checkIndex(arrLen int, index int32) {
	if index < 0 || index >= int32(arrLen) {
		panic("java.lang.ArrayIndexOutBoundException")
	}
}
